import json
from os import PathLike
from typing import Any, IO, TypeVar
from warnings import warn

from zkl_pyutils_registries import UNSET

from zkl_pyutils_serialization.basics import context
from zkl_pyutils_serialization.convert import convert
from zkl_pyutils_serialization.convertors import JsonStringFormat, JsonValue, JsonValueFormat, PyObjectFormat
from zkl_pyutils_serialization.tags import FilePath

T = TypeVar('T')


def dump_json_value(value: T) -> JsonValue:
    return convert(value,
        src_format=PyObjectFormat,
        dst_format=JsonValueFormat)


def parse_json_value(value: JsonValue, dst_typehint: type[T] | Any = UNSET) -> T:
    return convert(value,
        src_format=JsonValueFormat,
        dst_format=PyObjectFormat,
        dst_typehint=dst_typehint)


def dump_json_string(value: T) -> str:
    return convert(value,
        src_format=PyObjectFormat,
        dst_format=JsonStringFormat)


def parse_json_string(value: str, dst_typehint: type[T] | Any = UNSET) -> T:
    return convert(value,
        src_format=JsonStringFormat,
        dst_format=PyObjectFormat,
        dst_typehint=dst_typehint)


def dump_json(value: T) -> str:
    warn("Use `dump_json_string()` instead.", DeprecationWarning)
    json_value = dump_json_value(value)
    return json.dumps(json_value)


def parse_json(value: str, dst_typehint: type[T] | Any = UNSET) -> T:
    warn("Use `parse_json_string()` instead.", DeprecationWarning)
    json_value = json.loads(value)
    return parse_json_value(json_value, dst_typehint)


def dump_and_write_json(value: T, io: IO):
    json_value = dump_json_value(value)
    json.dump(json_value, io)


def read_and_parse_json(io: IO, dst_typehint: type[T] | Any = UNSET) -> T:
    json_value = json.load(io)
    return parse_json_value(json_value, dst_typehint)


def dump_and_save_json(value: T, path: PathLike | str):
    with open(path, 'w') as io, context({FilePath: path}):
        dump_and_write_json(value, io)


def load_and_parse_json(path: PathLike | str, dst_typehint: type[T] | Any = UNSET) -> T:
    with open(path, 'r') as io, context({FilePath: path}):
        return read_and_parse_json(io, dst_typehint)
